<?php

namespace scaffold\auth;

use scaffold\exception\AuthException;
use think\Config;
use think\facade\Request;

/**
 * 签名验证
 * @author edward <edward@localhost.com>
 */
class Api
{
    /**
     * 认证标识
     * @var string
     */
    protected $auth;

    /**
     * 请求时间戳
     * @var integer/string
     */
    protected $timestamp;

    /**
     * 随机字符串
     * @var string
     */
    protected $randStr;

    /**
     * 约定标识
     * @var string
     */
    protected $sign;

    /**
     * 超时时长
     * @var integer
     */
    protected $timeoutSec = 60;

    /**
     * 析构加载
     * auth认证、请求时间戳、随机字符串、sign约定标识
     * @author edward edward@localhost.com
     * @DateTime 2020-08-13
     * @version  1.0.0.0
     *
     * @param    string     $sign      [sign约定标识]
     * @param    string     $auth      [auth认证]
     * @param    string     $timestamp [请求时间戳]
     * @param    string     $randStr   [随机字符串]
     */
    public function __construct($sign = '', $auth = '', $timestamp = 0, $randStr = '')
    {
        $this->sign      = $sign;
        $this->auth      = empty($auth) ? Request::header('Api-Auth') : $auth;
        $this->timestamp = empty($timestamp) ? Request::header('Api-Timestamp') : $timestamp;
        $this->randStr   = empty($randStr) ? Request::header('Api-Random-Str') : $randStr;
    }

    /**
     * 设置认证参数
     * @author edward edward@localhost.com
     * @DateTime 2020-08-17
     * @version  1.0.0.0
     *
     * @param    string     $auth      auth认证
     * @param    integer    $timestamp 请求时间戳
     * @param    string     $randStr   随机字符串
     * @return   $this
     */
    public function param($auth = '', $timestamp = 0, $randStr = '')
    {
        $this->auth      = empty($auth) ? $this->auth : $auth;
        $this->timestamp = empty($timestamp) ? $this->timestamp : $timestamp;
        $this->randStr   = empty($randStr) ? $this->randStr : $randStr;
    }

    /**
     * 设置约定标志
     * @author edward edward@localhost.com
     * @DateTime 2020-08-17
     * @version  1.0.0.0
     *
     * @param    string     $sign 约定标志
     * @return   $this
     */
    public function sign($sign)
    {
        $this->sign = $sign;
        return $this;
    }

    /**
     * 认证
     * @author edward edward@localhost.com
     * @DateTime 2020-07-21
     * @version  1.0.0.0
     *
     * @return   bool/exception
     */
    public function check()
    {
        if (empty($this->sign)) {
            throw new AuthException('sign标识不能为空');
        }
        if (empty($this->auth) || empty($this->timestamp) || empty($this->randStr)) {
            throw new AuthException('缺少认证参数');
        }
        if (abs(time() - $this->timestamp) >= $this->timeoutSec) {
            throw new AuthException('认证超时');
        }
        $serverAuth = $this->encode($this->sign, $this->timestamp, $this->randStr);
        if ($serverAuth !== $this->auth) {
            throw new AuthException('认证标识错误');
        }
        return true;
    }

    /**
     * 生成认证标识 auth
     * @author edward edward@localhost.com
     * @DateTime 2020-07-21
     * @version  1.0.0.0
     *
     * @param    string     $sign      约定标识
     * @param    string     $timestamp 时间戳
     * @param    string     $randStr   随机字符串
     * @return   string                auth认证标识
     */
    private function encode($sign, $timestamp, $randStr)
    {
        $arr = array($sign, $timestamp, $randStr);
        sort($arr);
        return md5(implode($arr, ''));
    }

    /**
     * TP 容器加载函数
     * @author edward edward@localhost.com
     * @DateTime 2020-08-19
     * @version  1.0.0.0
     *
     * @param    Config     $config [description]
     * @return   [type]             [description]
     */
    public static function __make(Config $config)
    {
        return new static($config->get('api.sign'));
    }
}
